<template>
  <div class="graph-analysis">
    <model-analysis-header
      :lasso-enable="lassoEnable"
      :box-selection-enable="boxSelectionEnable"
      :selected-path="selectedPath"
      :is-label-show="isLabelShow"
      @toggle-label="toggleLabel"
      @box-lasso-enable="boxLassoEnable"
      @toggle-path="togglePath"
      @delete-element="deleteGraphElement"
      @save-widget="graphSaveWidget"
    />
    <div class="analysis-graph-content">
      <!-- left bar -->
      <model-analysis-left-bar
        :model-nodes="modelNodes"
        :select-nodes-info="selectedNodesByOrder"
        @confirm-path="confirmQueryPath"
        @cancel-path="cancelQueryPath"
        @cancel-search="cancelSearch"
        @confirm-search="confirmSearch"
        @cancel-cluster="cancelQueryCluster"
        @confirm-cluster="confirmQueryCluster"
      />

      <!-- loading -->
      <div v-show="loadingStatus" class="query-loading">
        <a-spin :tip="loadingText" size="large" :delay="1000">
          <!-- <div class="spin-content">-->
          <!-- 我的描述文案是自定义的。。。-->
          <!-- </div>-->
        </a-spin>
      </div>

      <!-- 按照百分比过滤节点 -->
      <sample-node-slider
        :node-show-count="nodeShowCount"
        @extracting-node="sampleNode"
      />

      <!-- 缩放 -->
      <graph-zoom-control
        :zoom-level="scaleLevel"
        @scale="changeScale"
        @scale-type-change="handleScaleTypeChange"
        @fit-view="fitView"
      />

      <!--  graph 图 -->
      <div
        id="model-analysis-root"
        ref="modelAnalysisRoot"
        class="model-analysis-root"
      ></div>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'

import ModelAnalysisHeader from '@/components/graph/model-analysis/ModelAnalysisHeader.vue' // header
import ModelAnalysisLeftBar from '@/components/graph/model-analysis/ModelAnalysisLeftBar.vue' // left bar
import SampleNodeSlider from '@/components/graph/model-analysis/SampleNodeSlider.vue' // 筛选
import GraphZoomControl from '@/components/graph/model-analysis/GraphZoomControl.vue' // 缩放

import GraphViewStore from '@/store/modules/graphview'
import GraphBuild from '@/store/modules/graphbuild'
import {
  graphQueryPath,
  graphBatchDeleteNodesAndLinks,
  graphQueryCluster,
  graphSaveWidget,
  graphUpdateNode,
  graphSearch,
} from '@/api/visualization'
import { cloneDeep } from 'lodash'
import G6 from '@antv/g6'
import { graphHullColorScheme } from '@/components/graph/graph-util'
import noPermissionPrompt from '@/components/graph/user-permissions'

@Component({
  components: {
    ModelAnalysisHeader,
    ModelAnalysisLeftBar,
    SampleNodeSlider,
    GraphZoomControl,
  },
})
export default class GraphAnalysisWithGSix extends Vue {
  @Prop() data: any
  @Prop() dataset: any
  @Prop() modelNodes: any // 构建途中的节点
  // private graphId: number | null = null  // graph ID

  private modelAnalysisGraph: any = null // 画布
  // graph组件参数
  public lassoEnable: boolean = false
  public boxSelectionEnable: boolean = false
  private selectedPath: boolean = false // 最短路径功能开关
  private selectedPath_firstNode: any = null // 最短路径的第一个节点
  private selectedNodesByOrder: any = [] // 记录节点的选择顺序，默认的选中节点按照初始化的顺序来排列，没有考虑点击的顺序
  public isLabelShow: boolean = false // 节点显示 label
  public selectNodesInfo: any = [] // 选择的节点
  public nodeShowCount: number = 100 // 显示节点的比例
  private loadingStatus: boolean = false // 图分析 loading 状态
  private loadingText: string = '' // loading 文案
  private isShowLabelByManual = false // 记录是否手动显示/隐藏标签
  private mousePosition: Array<number> = [0, 0] // 鼠标位置

  public get isBuildingGraph() {
    return GraphBuild.isBuildingGraph
  }

  @Watch('isBuildingGraph')
  private watchIsBuildingGraph(value: number) {
    if (value === 2) {
      this.loadingText = '正在构建中，请稍等...'
      this.loadingStatus = true
    } else if (value === 3 || value === 4) {
      this.loadingStatus = false
    }
  }

  public get graphSaveStatus() {
    return GraphBuild.graphSaveStatus
  }

  @Watch('graphSaveStatus', { deep: true })
  private watchGraphSaveStatus(newStatus: any) {
    // const that: any = this
    // 存储失败， 定时重新存储
    if (newStatus.type === 3) {
      this.$message.error('节点信息存储失败!')
      // setTimeout(() => {that.saveGraph()}, 1000 * 60)
    }
  }

  // 是否没有编辑权限（图构建创建者）
  public get isGraphSelfCreate() {
    return GraphBuild.isGraphSelfCreate
  }

  public mounted() {
    // this.graphId = Number.parseInt(localStorage.getItem('graphId') || '0', 10)  // graphId 滞后 修改 computed
    this.selfRegisterBehavior() // 自定义选择边的交互 Behavior
  }

  public get graphId() {
    return Number.parseInt(localStorage.getItem('graphId') || '0', 10)
  }

  private havePositionInfo: boolean = false // 是否有位置信息
  @Watch('data')
  public watchData(value: any) {
    this.nodeShowCount = 100
    const newDataFirstNode: any = cloneDeep(value.nodes[0])
    // 说明有位置信息
    this.havePositionInfo = 'x' in newDataFirstNode && 'y' in newDataFirstNode
    // 没有位置信息, 有力导行为， 等待存储
    if (!this.havePositionInfo) {
      this.loadingText = '正在加载中，请稍等...'
      this.loadingStatus = true
      GraphBuild.setGraphSaveStatus({
        type: 1,
        lastSaveTime: new Date().getTime(),
      })
    }

    if (value.nodes.length > 0) {
      if (!this.modelAnalysisGraph) {
        this.initGraphAnalysis()
      } else {
        if (this.havePositionInfo) {
          this.modelAnalysisGraph.updateLayout({})
        } else {
          this.modelAnalysisGraph.updateLayout(this.layout)
        }
        this.modelAnalysisGraph.read({
          nodes: this.data.nodes,
          edges: this.data.links,
        })
      }

      this.updateNodeLabelVisible()
      this.hideEdgeLabel()
    }
  }

  private layout: any = {
    type: 'force',
    preventOverlap: true,
    nodeSpacing: 2,
    alpha: 0.3, // 当前的迭代收敛阈值 0.3
    alphaDecay: 0.028, // 迭代阈值的衰减率 0.028
    alphaMin: 0.001, // 停止迭代的阈值 0.001
    workerScriptURL: `${window.location.protocol}//${window.location.host}/js/layout.min.js`,
    workerEnabled: true,
    onLayoutEnd: () => {
      this.saveGraph()
    },
    onTick: () => {
      console.log('force layout iteration callback')
    },
  }

  private saveGraph() {
    this.loadingStatus = false
    GraphBuild.setIsBuildingGraph(3) // 构建结束成功
    const graphInfo: any = this.modelAnalysisGraph.save()
    const size: number[] = [
      this.modelAnalysisGraph.getWidth(),
      this.modelAnalysisGraph.getHeight(),
    ]
    this.$emit('save-graph', { graphInfo, size })
  }

  /**
   * g6 点击选择边, 自定义 Behavior
   */
  private selfRegisterBehavior() {
    const that: any = this
    G6.registerBehavior('select-edge', {
      getDefaultCfg() {
        return {
          multiple: true, // 允许多选
        }
      },
      getEvents() {
        return {
          'edge:click': 'onEdgeClick',
        }
      },
      onEdgeClick($event: any) {
        const graph: any = that.modelAnalysisGraph
        const { item, originalEvent } = $event
        if (item.hasState('selected')) {
          graph.setItemState(item, 'selected', false)
          return
        }
        // this 上即可取到配置，如果不允许多个 'active'，先取消其他节点的 'active' 状态
        if (!this.multiple || (this.multiple && !originalEvent.shiftKey)) {
          that.removeNodeSelectedState()
          that.removeEdgeSelectedState()
        }
        // 置点击的节点状态 'active' 为 true
        graph.setItemState(item, 'selected', true)
      },
    })
  }

  /**
   *
   */
  private removeEdgeSelectedState() {
    this.modelAnalysisGraph
      .findAllByState('edge', 'selected')
      .forEach((edge: any) => {
        this.modelAnalysisGraph.setItemState(edge, 'selected', false)
      })
  }

  private removeNodeSelectedState() {
    this.modelAnalysisGraph
      .findAllByState('node', 'selected')
      .forEach((node: any) => {
        this.modelAnalysisGraph.clearItemStates(node, ['selected', 'active'])
      })
  }

  private contextMenu = new G6.Menu({
    // @ts-ignore
    getContent($event: any, analysisGraph: any) {
      const { model } = $event.item._cfg
      const selectNodesInfo = analysisGraph.findAllByState('node', 'selected')
      const selectNodesId: any[] =
        selectNodesInfo.length > 0
          ? selectNodesInfo.map((node: any) => node._cfg.model.id)
          : []
      let menuDom: string = '<li data-way="2" class="sub-menu-item">删除</li>'
      // 当具备查询条件
      if (selectNodesId.length === 2 && selectNodesId.includes(model.id)) {
        menuDom = `<li data-way="1" class="sub-menu-item">搜索最短路径</li>${menuDom}`
      }
      const outDiv = document.createElement('div')
      outDiv.style.width = '120px'
      outDiv.innerHTML = `<ul class="node-edge-menu">${menuDom}</ul>`
      return outDiv
    },
    handleMenuClick: this.nodeMenuClick,
    shouldBegin: () => {
      return this.isGraphSelfCreate // 访客 - false
    },
    offsetX: 16 + 10,
    offsetY: 0,
    itemTypes: ['node', 'edge'],
  })

  private tooltip = new G6.Tooltip({
    itemTypes: ['node', 'edge'],
    offsetX: 10,
    offsetY: 10,
    getContent(event: any) {
      const model: any = event.item.getModel()
      let attributesDom: string = '<div class="attr-key-value"> -- 空 -- </div>'
      if (model.attrs.length > 0) {
        attributesDom = ''
        model.attrs.forEach((attribute: any) => {
          attributesDom += `<div class="attr-key-value">${attribute.key}: ${attribute.value}</div>`
        })
      }
      const isEdge: boolean = event.item.getType() === 'edge'
      return `
        <div class="tooltip-section">
          <div class="tooltip-title">${isEdge ? '边' : '节点'}属性</div>
          ${attributesDom}
        </div>
      `
    },
  })

  /**
   * way: 1 路径查询， 2 删除
   */
  public nodeMenuClick(target: any, item: any) {
    const type: string = `${item._cfg.type}-${target.dataset.way}`
    if (type === 'node-1') {
      const selectNodesInfo = this.modelAnalysisGraph.findAllByState(
        'node',
        'selected'
      )
      const selectNodesId: any[] = selectNodesInfo.map(
        (node: any) => node._cfg.model.id
      )
      this.queryNodePath(selectNodesId, 1, -1, 1)
    } else if (type === 'node-2') {
      this.deleteGraphElement({ type: 'node', item })
    } else if (type === 'edge-2') {
      this.deleteGraphElement({ type: 'edge', item })
    }
  }

  /**
   * 初始化 画布
   */
  public initGraphAnalysis() {
    const graphElement = this.$refs.modelAnalysisRoot as HTMLElement
    // 基础配置
    const g6Setting: any = {
      container: 'model-analysis-root',
      width: graphElement.offsetWidth,
      height: graphElement.offsetHeight,
      fitCenter: true,
      layout: this.layout,
      animate: false,
      minZoom: 0.01,
      maxZoom: 10,
    }
    // 布局
    if (this.havePositionInfo) {
      g6Setting.layout = {}
    }
    // 各行为模式都支持的 Behavior 交互事件的机制
    const generalBehavior: any[] = [
      'drag-node',
      'select-edge',
      {
        type: 'click-select',
        // 主要是 为点击选择边
        shouldBegin: ($event: any) => {
          if (!$event.originalEvent.shiftKey) {
            this.removeEdgeSelectedState()
          }
          return true
        },
      },
      { type: 'zoom-canvas', enableOptimize: true },
    ]
    this.modelAnalysisGraph = new G6.Graph({
      ...g6Setting,
      plugins: [this.contextMenu, this.tooltip],
      modes: {
        default: [
          ...generalBehavior,
          { type: 'drag-canvas', enableOptimize: true },
        ],
        brushSelect: [
          ...generalBehavior,
          {
            type: 'brush-select',
            trigger: 'drag',
            includeEdges: false,
            onSelect: (selectInfo: any) => {
              console.log(selectInfo, '刷选中的节点')
            },
          },
        ],
        lassoSelect: [
          ...generalBehavior,
          {
            type: 'lasso-select',
            trigger: 'drag',
            includeEdges: false,
            onSelect: (selectInfo: any) => {
              console.log(selectInfo, '套索中的节点')
            },
          },
        ],
      },
      defaultNode: {
        size: 20,
      },
      nodeStateStyles: {
        putAsh: {
          fillOpacity: 0.3,
        },
      },
      defaultEdge: {
        type: 'line',
        style: {
          lineWidth: 2,
          stroke: '#D9D9D9',
          endArrow: false,
        },

        labelCfg: {
          style: {
            fillOpacity: 1,
          },
        },
      },
      edgeStateStyles: {
        hover: {
          lineWidth: 2,
          cursor: 'pointer',
          stroke: '#A8AEFF',
        },
        selected: {
          lineWidth: 2,
          stroke: '#588cf7',
          shadowBlur: 0,
        },
        click: {
          lineWidth: 3,
          stroke: '#A8AEFF',
        },
      },
    })

    this.modelAnalysisGraph.data({
      nodes: this.data.nodes,
      edges: this.data.links,
    })
    this.modelAnalysisGraph.render()
    // TODO 暂时关闭关闭局部渲染， 解决 label edge 移动残影问题
    this.modelAnalysisGraph.get('canvas').set('localRefresh', false)
    this.handleGraphNodeEvent()
    this.handleGraphCanvasEvent()
  }

  // 节点事件
  public handleGraphNodeEvent() {
    this.modelAnalysisGraph.on('node:mouseenter', ($event: any) => {
      if (!this.isGraphSelfCreate) {
        return
      }
      const nodeItem = $event.item
      this.modelAnalysisGraph.setItemState(nodeItem, 'hover', true)
    })

    this.modelAnalysisGraph.on('node:mouseout', ($event: any) => {
      const nodeItem = $event.item
      this.modelAnalysisGraph.clearItemStates(nodeItem, 'hover')
    })

    // this.modelAnalysisGraph.on('afterupdateitem', ($event: any) => {
    //   console.log($event)
    //   // 存在社团检测轮廓
    //   if(this.hullObjectList.length > 0) {
    //     this.updateHull($event.item._cfg.model.id)
    //   }
    // })

    this.modelAnalysisGraph.on('node:dragend', ($event: any) => {
      if (!this.isGraphSelfCreate) {
        return
      }
      this.updateNodePosition($event)
      // 存在社团检测轮廓
      if (this.hullObjectList.length > 0) {
        this.updateHull($event.item._cfg.model.id)
      }
    })
  }

  // 画布事件
  public handleGraphCanvasEvent() {
    this.modelAnalysisGraph.on('canvas:click', () => {
      this.graphClearItemStates()
    })

    this.modelAnalysisGraph.on('canvas:mousemove', (event: any) => {
      this.mousePosition = [event.canvasX, event.canvasY]
    })

    this.modelAnalysisGraph.on('nodeselectchange', ($event: any) => {
      const { nodes } = $event.selectedItems

      if (nodes.length <= 1) {
        // 没有选中节点或只有一个时，重新初始化排序节点数组
        this.selectedNodesByOrder = nodes
      } else {
        // 将新增加的节点排到后面
        // 1.删除取消选中的节点
        for (let i = this.selectedNodesByOrder.length - 1; i >= 0; i -= 1) {
          const node = this.selectedNodesByOrder[i]
          if (!nodes.includes(node)) {
            this.selectedNodesByOrder.splice(i, 1)
          }
        }

        // 2.增加新的节点
        nodes.forEach((node: any) => {
          if (!this.selectedNodesByOrder.includes(node)) {
            this.selectedNodesByOrder.push(node)
          }
        })
      }

      // 如果打开最短路径的选择标记，则自动记录选择的节点，达到两个节点后即自动计算最短路径
      if (this.selectedPath) {
        if (this.selectedPath_firstNode) {
          nodes.push(this.selectedPath_firstNode)
        } else {
          ;[this.selectedPath_firstNode] = nodes
        }

        // 重新选择节点时，将原有的高亮的最短路径清除
        if (nodes.length === 1) {
          this.graphClearItemStates()
          this.modelAnalysisGraph.setItemState(nodes[0], 'highlighted', true)
        }

        // 选择完两个节点后，开始查找最短路径
        if (nodes.length === 2) {
          const selectNodesId: any = nodes.map((node: any) => node._cfg.id)
          this.queryNodePath(selectNodesId, 1, -1, 1)
          this.selectedPath_firstNode = null
        }
      }

      this.selectNodesInfo = nodes
    })

    this.modelAnalysisGraph.on('wheel', (event: any) => {
      const { wheelDeltaY } = event
      if (wheelDeltaY > 0) {
        this.changeScale(this.scaleLevel + 0.05)
      } else if (wheelDeltaY < 0) {
        this.changeScale(this.scaleLevel - 0.05)
      }
    })
  }

  /**
   * 清除画布元素的状态
   */
  public graphClearItemStates() {
    this.modelAnalysisGraph.getNodes().forEach((node: any) => {
      this.modelAnalysisGraph.clearItemStates(node)
    })
    this.modelAnalysisGraph.getEdges().forEach((edge: any) => {
      this.modelAnalysisGraph.clearItemStates(edge)
    })
  }

  /**
   * 高亮显示路径
   */
  public highlightPath(path: any) {
    const pathNodeMap: any = {}
    // 所有节点高亮
    path.forEach((id: any) => {
      const pathNode = this.modelAnalysisGraph.findById(id)
      this.modelAnalysisGraph.setItemState(pathNode, 'highlighted', true)
      pathNodeMap[id] = true
    })
    // 所有 path 边 高亮显示
    this.modelAnalysisGraph.getEdges().forEach((edge: any) => {
      const edgeModel = edge.getModel()
      const { source, target } = edgeModel
      const sourceInPathIdx = path.indexOf(source)
      const targetInPathIdx = path.indexOf(target)
      if (sourceInPathIdx !== -1 && targetInPathIdx !== -1) {
        this.modelAnalysisGraph.setItemState(edge, 'highlight', true)
      } else {
        this.modelAnalysisGraph.setItemState(edge, 'inactive', true)
      }
    })
    // 其他节点 置灰
    this.modelAnalysisGraph.getNodes().forEach((node: any) => {
      if (!pathNodeMap[node.getID()]) {
        this.modelAnalysisGraph.setItemState(node, 'putAsh', true)
      }
    })
  }

  /**
   * 高亮显示搜索的节点
   */
  public highlightSearchNode(ids: any) {
    // 所有节点高亮
    this.modelAnalysisGraph.getNodes().forEach((node: any) => {
      if (ids.includes(node._cfg.model.id)) {
        this.modelAnalysisGraph.setItemState(node, 'highlighted', true)
      } else {
        this.modelAnalysisGraph.setItemState(node, 'putAsh', true)
      }
    })
  }

  /**
   * 拖拽节点, 更新位置信息,后端同步,
   */
  public updateNodePosition($event: any) {
    const dragNode: any = cloneDeep($event.item._cfg.model)
    dragNode.style.stroke = dragNode.style.fill
    dragNode.style.r = 10

    const data: any = {
      graphId: this.graphId,
      node: dragNode,
    }

    graphUpdateNode({ data }).then((response: any) => {
      if (response.data.code !== 100) {
        this.$message.warning('节点位置信息同步失败！')
      }
    })
  }

  /**
   *  =========== header bar   ===============
   */

  /**
   * header tool
   * 节点 label
   */
  public toggleLabel() {
    this.isShowLabelByManual = true
    this.isLabelShow = !this.isLabelShow
    this.updateNodeLabelVisible()
  }

  private updateNodeLabelVisible() {
    const nodes = this.modelAnalysisGraph.getNodes()
    for (let j = 0, nodeLength = nodes.length; j < nodeLength; j += 1) {
      const container = nodes[j].getContainer()
      const children = container.get('children')
      children.forEach((child: any) => {
        if (child.cfg.type === 'text') {
          if (this.isLabelShow) {
            child.show()
          } else {
            child.hide()
          }

          child.set('ori-visibility', this.isLabelShow)
        }
      })
    }
  }

  private hideEdgeLabel() {
    const edges = this.modelAnalysisGraph.getEdges()
    for (let i = 0, edgeLength = edges.length; i < edgeLength; i += 1) {
      const container = edges[i].getContainer()
      const children = container.get('children')
      children.forEach((child: any) => {
        if (child.cfg.type === 'text') {
          child.hide()
        }
      })
    }
  }

  /**
   * 套索， 和 框选 1-框选， 2-套索  4-关闭所有
   */
  public boxLassoEnable(mode: number) {
    this.closeToolBarOtherFunction(mode)
    if (mode === 1 && !this.boxSelectionEnable) {
      this.boxSelectionEnable = true
      this.modelAnalysisGraph.setMode('brushSelect')
    } else if (mode === 2 && !this.lassoEnable) {
      this.lassoEnable = true
      this.modelAnalysisGraph.setMode('lassoSelect')
    }
  }

  // 最短路径
  public togglePath() {
    this.selectedPath = !this.selectedPath
    this.$nextTick(() => {
      if (this.selectedPath) {
        this.closeToolBarOtherFunction(3)
      } else {
        this.closeToolBarOtherFunction(4)
      }
    })
  }

  /**
   * 删除节点和边的, 批量删除
   */
  public deleteGraphElement(deleteInfo: any) {
    if (!this.isGraphSelfCreate) {
      noPermissionPrompt()
      return
    }
    this.selectNodesInfo = this.modelAnalysisGraph.findAllByState(
      'node',
      'selected'
    )
    const selectEdgesInfo: any = this.modelAnalysisGraph.findAllByState(
      'edge',
      'selected'
    )
    if (
      this.selectNodesInfo.length === 0 &&
      selectEdgesInfo.length === 0 &&
      deleteInfo.type === 'icon'
    ) {
      return
    }
    // 选择边 id
    const deleteEdgesId: any[] =
      selectEdgesInfo.length > 0
        ? selectEdgesInfo.map((edge: any) => edge._cfg.model.id)
        : []
    if (deleteInfo.type === 'edge') {
      deleteEdgesId.push(deleteInfo.item._cfg.model.id)
    } // 右键边删除
    // 选择的节点 id
    const deleteNodesId: any[] =
      this.selectNodesInfo.length > 0
        ? this.selectNodesInfo.map((node: any) => {
            // 根据节点查找辐射边
            node._cfg.edges.forEach((edge: any) => {
              if (!deleteEdgesId.includes(edge._cfg.model.id)) {
                deleteEdgesId.push(edge._cfg.model.id)
              }
            })
            return node._cfg.model.id
          })
        : []
    // 右键边删除
    if (deleteInfo.type === 'node') {
      const currentNodeId: any = deleteInfo.item._cfg.model.id
      if (!deleteNodesId.includes(currentNodeId)) {
        deleteNodesId.push(currentNodeId)
      }
      deleteInfo.item._cfg.edges.forEach((edge: any) => {
        if (!deleteEdgesId.includes(edge._cfg.model.id)) {
          deleteEdgesId.push(edge._cfg.model.id)
        }
      })
    }
    const data: any = {
      graphId: this.graphId,
      nodeIds: deleteNodesId,
      linkIds: deleteEdgesId,
    }
    this.batchDeleteApi(data) // 走公共方法
  }

  /**
   * top and left tool bar 关闭其他功能
   * @param current 当前功能 1-框选 2-套索 3-最短路径 4-关闭 top tool bar 所有
   * 在确认
   */
  public closeToolBarOtherFunction(current: any) {
    this.leftToolBarFunction()
    switch (current) {
      case 1:
        this.selectedPath = false
        this.lassoEnable = false
        this.selectedPath_firstNode = null
        break
      case 2:
        this.boxSelectionEnable = false
        this.selectedPath = false
        this.selectedPath_firstNode = null
        break
      case 3:
        this.lassoEnable = false
        this.boxSelectionEnable = false
        this.modelAnalysisGraph.setMode('default')
        break
      default:
        this.lassoEnable = false
        this.boxSelectionEnable = false
        this.selectedPath = false
        this.modelAnalysisGraph.setMode('default')
        this.selectedPath_firstNode = null
        break
    }
  }

  /**
   * left tool bar 功能
   * 清除社团检测
   * 清除节点状态（搜索和路径）
   */
  public leftToolBarFunction() {
    if (this.modelAnalysisGraph.getHulls()) {
      this.cancelQueryCluster()
    }
    this.graphClearItemStates()
  }

  public get graphSize() {
    return GraphViewStore.graphSize
  }

  @Watch('graphSize', { deep: true })
  public updateGraphHeight(size: any) {
    if (this.modelAnalysisGraph) {
      this.modelAnalysisGraph.changeSize(size.width, size.height)
    }
  }

  public savePopoverShow: boolean = false // 保存 popover
  /**
   * 保存视图
   */
  public graphSaveWidget(graphName: string) {
    if (GraphViewStore.graphAnalysisData?.nodes?.length > 0) {
      graphSaveWidget({ data: { graphId: this.graphId, graphName } }).then(
        (response: any) => {
          if (response.data.code === 100) {
            this.savePopoverShow = false
            this.$message.success('保存到可视化视图成功！')
          }
        }
      )
    } else {
      this.savePopoverShow = false
      this.$message.warning('无需要保存的 graph, 请先进行构建！')
    }
  }

  /**
   * ========= 画布左上角 功能 start ===============
   */

  /**
   * left bar
   * 取消搜索 $emit
   */
  public cancelSearch() {
    this.graphClearItemStates()
  }

  /**
   * left bar
   * 搜索 $emit
   */
  public confirmSearch(parameters: any) {
    const data: any = {
      graphId: this.graphId,
      ...parameters,
    }
    this.closeToolBarOtherFunction(4) // 关闭其他功能， 清除节点状态
    this.loadingText = '搜索中...'
    this.loadingStatus = true
    graphSearch({ data })
      .then((response: any) => {
        if (response.data.code === 100) {
          const searchResult = response.data.result.main
          if (searchResult.length > 0) {
            this.highlightSearchNode(searchResult)
            this.$message.success('搜索成功！', 1)
          } else {
            this.$message.success('搜索成功, 搜索结果为空！', 2)
          }
        } else {
          this.$message.warning(response.data.message, 0.8)
        }
      })
      .finally(() => {
        this.loadingStatus = false
      })
  }

  /**
   * left bar
   * 查询路径 $emit
   */
  public confirmQueryPath(queryInfo: any) {
    this.queryNodePath(
      queryInfo.selectNodesId,
      queryInfo.type,
      queryInfo.maxStep,
      queryInfo.from
    )
  }

  /**
   * left bar
   * 取消查询路径 $emit
   */
  public cancelQueryPath() {
    this.graphClearItemStates()
  }

  /**
   * 两点之间的路径查询  公用方法 tool 左上角  节点 menu
   * @param selectNodesId
   * @param type 查询模式 1-最短路径 2- 所有
   * @param maxStep 最大步长
   * @param from 查询方式 1-不限步长， 右键， 右上角 icon 2-左上角指定步长
   */
  public queryNodePath(
    selectNodesId: any,
    type: number,
    maxStep: number,
    from: number
  ) {
    const data: any = {
      graphId: this.graphId,
      srcId: selectNodesId[0],
      tarId: selectNodesId[1],
      param: {
        type: type === 1 ? 'shortest' : 'all',
      },
    }
    // 配置项过来
    if (from === 2) {
      data.param.maxStep = maxStep
      this.closeToolBarOtherFunction(4) // 关闭其他功能， 清除节点状态
    } else {
      this.closeToolBarOtherFunction(3) // 关闭其他功能， 清除节点状态
    }
    this.loadingText = '路径查询中...'
    setTimeout(() => {
      this.loadingStatus = true
    }, 5) // 防止鼠标在节点的事件来不及释放
    graphQueryPath({ data })
      .then((response: any) => {
        if (response.data.code === 100) {
          if (response.data.result.length > 0) {
            this.$message.success('路径查询成功！', 0.8)
            this.highlightPath([...response.data.result[0]])
          } else {
            this.$message.success('路径查询成功, 查询结果为空！', 1)
          }
        } else {
          this.$message.warning(response.data.message, 0.8)
        }
      })
      .finally(() => {
        this.loadingStatus = false
      })
  }

  /**
   * left bar
   * 取消社团检测 $emit
   */
  public cancelQueryCluster() {
    this.modelAnalysisGraph.removeHulls()
  }

  /**
   * left bar
   * 社团检测 $emit
   */
  public confirmQueryCluster(maxIter: number) {
    let selectNodesId: any = this.selectNodesInfo.map(
      (node: any) => node._cfg.model.id
    )
    if (selectNodesId.length <= 0) {
      selectNodesId = this.data.nodes.map((node: any) => node.id)
    }
    const data: any = {
      graphId: this.graphId,
      nodeIds: selectNodesId,
      param: {
        maxIter,
      },
    }
    this.closeToolBarOtherFunction(4) // 关闭其他功能， 清除节点状态
    this.loadingText = '社团检测中...'
    this.loadingStatus = true
    graphQueryCluster({ data })
      .then((response: any) => {
        if (response.data.code === 100) {
          const clusterLength: number = Object.keys(response.data.result).length
          if (clusterLength > 0) {
            this.$message.success('社团检测成功！', 0.8)
            this.hullList = response.data.result
            this.createGraphHull(response.data.result)
          } else {
            this.$message.success('社团检测成功, 结果为空！', 1)
          }
        } else {
          this.$message.warning(response.data.message, 0.8)
        }
      })
      .finally(() => {
        this.loadingStatus = false
      })
  }

  public hullList: any = {} // 社团检测结果 list
  public hullObjectList: any[] = [] // 社团检测轮廓实例列表

  /**
   * 根据社团检测结果 创建社团 轮廓实例
   */
  public createGraphHull(clusters: any) {
    // 调接口时 移除当前图上所有轮廓实例
    this.hullObjectList = [] // 清空 轮廓实例
    Object.keys(clusters).forEach((key: any, index: number) => {
      const colorIndex: number = (index + 5) % (graphHullColorScheme.length - 1)
      const colorScheme: any = graphHullColorScheme[colorIndex]
      if (clusters[key].length >= 2) {
        const hullObject = this.modelAnalysisGraph.createHull({
          id: key,
          type: 'bubble',
          members: clusters[key],
          padding: 5,
          style: {
            fill: colorScheme.fill,
            stroke: colorScheme.stroke,
          },
        })
        this.hullObjectList.push(hullObject)
      }
    })
  }

  /**
   * 拖动节点 更新社团检测的轮廓
   * @param id
   */
  private updateHull(id: string) {
    // todo 换一种查找方式, delete this.hullObjectList
    // let currentHull: any = null
    // const hulls: any = this.modelAnalysisGraph.getHulls()
    // Object.keys(this.hullList).forEach((key: any) => {
    //   if(this.hullList[key].includes(id)) {
    //     currentHull = hulls[key]
    //   }
    // })
    const currentHull: any = this.hullObjectList.find((hull: any) =>
      this.hullList[hull.id].includes(id)
    )
    currentHull.updateData(currentHull.members, [])
    // this.hullObjectList.forEach((hull: any) => {
    //   if(hull.members.includes(id)) {
    //     console.log(hull.members.includes(id), hull.members)
    //     hull.updateData(hull.members)
    //   }
    // })
  }

  /**
   * ========= 画布左上角 功能 end ===============
   */

  public sampleNode(proportion: any) {
    // this.$emit('extracting-node', proportion)
    const sampleNodeIdList = this.modelAnalysisGraph
      .getNodes()
      .map((node: any) => {
        return node.getID()
      })
    sampleNodeIdList.forEach((nodeId: string) => {
      const node = this.modelAnalysisGraph.findById(nodeId)
      if (!node.isVisible()) {
        this.modelAnalysisGraph.showItem(nodeId)
      }
    })

    const sampleNodeTotal = Math.ceil(
      ((100 - proportion) * sampleNodeIdList.length) / 100
    )
    while (sampleNodeIdList.length > sampleNodeTotal) {
      const randomIndex = Math.round(Math.random() * sampleNodeIdList.length)
      sampleNodeIdList.splice(randomIndex, 2)
    }
    sampleNodeIdList.forEach((nodeId: string) => {
      this.modelAnalysisGraph.hideItem(nodeId)
    })
  }

  /**
   * 画布内容适应视口
   */
  public fitView(padding: number) {
    this.modelAnalysisGraph.fitView(padding)
  }

  // 画布缩放比例
  private scaleLevel: number = 1
  // 缩放类型：几何缩放geometry/语法缩放semantics
  private scaleType: string = 'geometry'

  // 处理缩放类型
  public handleScaleTypeChange(newScaleType: string) {
    this.scaleType = newScaleType

    if (newScaleType === 'geometry') {
      this.handleSemanticsScale(1)
    } else if (newScaleType === 'semantics') {
      this.hanldeGeometryScale(1)
    }
    this.scaleLevel = 1 // 切换缩放方式后回到最初状态
    this.modelAnalysisGraph.fitCenter() // 布局结束后居中显示
  }

  // 缩放比例更改
  public changeScale(scale: any) {
    // 最小20%
    scale = Math.max(scale, 0.2)
    if (!this.isShowLabelByManual) {
      if (
        (scale >= 1.3 && !this.isLabelShow) ||
        (scale < 1.3 && this.isLabelShow)
      ) {
        this.isLabelShow = !this.isLabelShow
        this.updateNodeLabelVisible()
      }
    }

    if (this.scaleType === 'geometry') {
      this.hanldeGeometryScale(scale)
    } else if (this.scaleType === 'semantics') {
      this.handleSemanticsScale(scale)
    }

    this.scaleLevel = scale
  }

  // 处理几何缩放
  public hanldeGeometryScale(scaleTo: any) {
    this.modelAnalysisGraph.zoomTo(scaleTo, {
      x: this.mousePosition[0],
      y: this.mousePosition[1],
    })
  }

  // 处理语义缩放
  public handleSemanticsScale(scale: any) {
    const nodes = this.modelAnalysisGraph.getNodes()
    nodes.forEach((node: any) => {
      const { style } = node.getModel()
      style.r = 10 / scale
      node.update({
        style,
      })
    })

    this.modelAnalysisGraph.zoomTo(scale, {
      x: this.mousePosition[0],
      y: this.mousePosition[1],
    })
  }

  /**
   * ================ 节点 menu 最短路径 删除节点 携带边 =============
   */

  /**
   * 批量删除的接口
   * @param data 参数
   * icon 删除   节点右键删除
   */
  public batchDeleteApi(data: any) {
    this.loadingText = '正在删除中...'
    this.loadingStatus = true
    graphBatchDeleteNodesAndLinks({ data })
      .then((response: any) => {
        if (response.data.code === 100) {
          const { nodeIds, linkIds } = data
          nodeIds.forEach((nodeId: any) => {
            this.modelAnalysisGraph.removeItem(nodeId)
          })
          linkIds.forEach((linkId: any) => {
            this.modelAnalysisGraph.removeItem(linkId)
          })
          // this.$emit('delete-item', data)
          this.$message.success('节点/边删除成功！')
        }
      })
      .finally(() => {
        this.loadingStatus = false
      })
  }
}
</script>

<style scoped lang="less">
@import '@/components/graph/model-analysis-with-g6';
</style>
